﻿/*
 * jrevdct.c
 *
 * Copyright (C) 1991, 1992, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file contains the basic inverse-DCT transformation subroutine.
 *
 * This implementation is based on an algorithm described in
 *   C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT
 *   Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics,
 *   Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991.
 * The primary algorithm described there uses 11 multiplies and 29 adds.
 * We use their alternate method with 12 multiplies and 32 adds.
 * The advantage of this method is that no data path contains more than one
 * multiplication; this allows a very simple and accurate implementation in
 * scaled fixed-point arithmetic, with a minimal number of shifts.
 * 
 * I've made lots of modifications to attempt to take advantage of the
 * sparse nature of the DCT matrices we're getting.  Although the logic
 * is cumbersome, it's straightforward and the resulting code is much
 * faster.
 *
 * A better way to do this would be to pass in the DCT block as a sparse
 * matrix, perhaps with the difference cases encoded.
 */

/*
Portions copyright (c) 2012 Eric Lasota

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

using System;
using System.Collections.Generic;

using System.Text;

namespace MPEGnet
{
    internal struct DCTROW
    {
        public short v0, v1, v2, v3, v4, v5, v6, v7;

        public DCTROW(short v0, short v1, short v2, short v3, short v4, short v5, short v6, short v7)
        {
            this.v0 = v0;
            this.v1 = v1;
            this.v2 = v2;
            this.v3 = v3;
            this.v4 = v4;
            this.v5 = v5;
            this.v6 = v6;
            this.v7 = v7;
        }

        public DCTROW(short singleVal)
        {
            v0 =
            v1 =
            v2 =
            v3 =
            v4 =
            v5 =
            v6 =
            v7 = singleVal;
        }

        public DCTROW(short[] shorts, int startIndex)
        {
            v0 = shorts[startIndex];
            v1 = shorts[startIndex + 1];
            v2 = shorts[startIndex + 2];
            v3 = shorts[startIndex + 3];
            v4 = shorts[startIndex + 4];
            v5 = shorts[startIndex + 5];
            v6 = shorts[startIndex + 6];
            v7 = shorts[startIndex + 7];
        }
    }

    internal struct DCTBLOCK
    {
        public DCTROW r0, r1, r2, r3, r4, r5, r6, r7;

        public DCTBLOCK(DCTROW r0, DCTROW r1, DCTROW r2, DCTROW r3, DCTROW r4, DCTROW r5, DCTROW r6, DCTROW r7)
        {
            this.r0 = r0;
            this.r1 = r1;
            this.r2 = r2;
            this.r3 = r3;
            this.r4 = r4;
            this.r5 = r5;
            this.r6 = r6;
            this.r7 = r7;
        }

        public DCTBLOCK(short singleVal)
        {
            r0 =
            r1 =
            r2 =
            r3 =
            r4 =
            r5 =
            r6 =
            r7 = new DCTROW(singleVal);
        }

        public DCTBLOCK(DCTROW singleVal)
        {
            r0 =
            r1 =
            r2 =
            r3 =
            r4 =
            r5 =
            r6 =
            r7 = singleVal;
        }

        public DCTBLOCK(short[] shorts)
        {
            r0 = new DCTROW(shorts, 0);
            r1 = new DCTROW(shorts, 8);
            r2 = new DCTROW(shorts, 16);
            r3 = new DCTROW(shorts, 24);
            r4 = new DCTROW(shorts, 32);
            r5 = new DCTROW(shorts, 40);
            r6 = new DCTROW(shorts, 48);
            r7 = new DCTROW(shorts, 56);
        }

        public static short RowElement(ref DCTROW row, int element)
        {
            switch (element & 7)
            {
                case 0:
                    return row.v0;
                case 1:
                    return row.v1;
                case 2:
                    return row.v2;
                case 3:
                    return row.v3;
                case 4:
                    return row.v4;
                case 5:
                    return row.v5;
                case 6:
                    return row.v6;
                case 7:
                    return row.v7;
            }
            return 0;
        }

        public short Element(int e)
        {
            switch (e >> 3)
            {
                case 0:
                    return RowElement(ref r0, e);
                case 1:
                    return RowElement(ref r1, e);
                case 2:
                    return RowElement(ref r2, e);
                case 3:
                    return RowElement(ref r3, e);
                case 4:
                    return RowElement(ref r4, e);
                case 5:
                    return RowElement(ref r5, e);
                case 6:
                    return RowElement(ref r6, e);
                case 7:
                    return RowElement(ref r7, e);
            }
            return 0;
        }

        public DCTBLOCK Transpose()
        {
            return new DCTBLOCK(
                new DCTROW(r0.v0, r1.v0, r2.v0, r3.v0, r4.v0, r5.v0, r6.v0, r7.v0),
                new DCTROW(r0.v1, r1.v1, r2.v1, r3.v1, r4.v1, r5.v1, r6.v1, r7.v1),
                new DCTROW(r0.v2, r1.v2, r2.v2, r3.v2, r4.v2, r5.v2, r6.v2, r7.v2),
                new DCTROW(r0.v3, r1.v3, r2.v3, r3.v3, r4.v3, r5.v3, r6.v3, r7.v3),
                new DCTROW(r0.v4, r1.v4, r2.v4, r3.v4, r4.v4, r5.v4, r6.v4, r7.v4),
                new DCTROW(r0.v5, r1.v5, r2.v5, r3.v5, r4.v5, r5.v5, r6.v5, r7.v5),
                new DCTROW(r0.v6, r1.v6, r2.v6, r3.v6, r4.v6, r5.v6, r6.v6, r7.v6),
                new DCTROW(r0.v7, r1.v7, r2.v7, r3.v7, r4.v7, r5.v7, r6.v7, r7.v7)
                );
        }
    }

    internal class jrevdct
    {
        /*
         * jrevdct.c
         *
         * Copyright (C) 1991, 1992, Thomas G. Lane.
         * This file is part of the Independent JPEG Group's software.
         * For conditions of distribution and use, see the accompanying README file.
         *
         * This file contains the basic inverse-DCT transformation subroutine.
         *
         * This implementation is based on an algorithm described in
         *     C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT
         *     Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics,
         *     Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991.
         * The primary algorithm described there uses 11 multiplies and 29 adds.
         * We use their alternate method with 12 multiplies and 32 adds.
         * The advantage of this method is that no data path contains more than one
         * multiplication; this allows a very simple and accurate implementation in
         * scaled fixed-point arithmetic, with a minimal number of shifts.
         * 
         * I've made lots of modifications to attempt to take advantage of the
         * sparse nature of the DCT matrices we're getting.    Although the logic
         * is cumbersome, it's straightforward and the resulting code is much
         * faster.
         *
         * A better way to do this would be to pass in the DCT block as a sparse
         * matrix, perhaps with the difference cases encoded.
         */

        /*
         * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT
         * on each column.    Direct algorithms are also available, but they are
         * much more complex and seem not to be any faster when reduced to code.
         *
         * The poop on this scaling stuff is as follows:
         *
         * Each 1-D IDCT step produces outputs which are a factor of sqrt(N)
         * larger than the true IDCT outputs.    The final outputs are therefore
         * a factor of N larger than desired; since N=8 this can be cured by
         * a simple right shift at the end of the algorithm.    The advantage of
         * this arrangement is that we save two multiplications per 1-D IDCT,
         * because the y0 and y4 inputs need not be divided by sqrt(N).
         *
         * We have to do addition and subtraction of the integer inputs, which
         * is no problem, and multiplication by fractional constants, which is
         * a problem to do in integer arithmetic.    We multiply all the constants
         * by CONST_SCALE and convert them to integer constants (thus retaining
         * CONST_BITS bits of precision in the constants).    After doing a
         * multiplication we have to divide the product by CONST_SCALE, with proper
         * rounding, to produce the correct output.    This division can be done
         * cheaply as a right shift of CONST_BITS bits.    We postpone shifting
         * as long as possible so that partial sums can be added together with
         * full fractional precision.
         *
         * The outputs of the first pass are scaled up by PASS1_BITS bits so that
         * they are represented to better-than-integral precision.    These outputs
         * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word
         * with the recommended scaling.    (To scale up 12-bit sample data further, an
         * intermediate INT32 array would be needed.)
         *
         * To avoid overflow of the 32-bit intermediate results in pass 2, we must
         * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26.    Error analysis
         * shows that the values given below are the most effective.
         */

        const int CONST_BITS = 13;
        const int PASS1_BITS = 1;        /* lose a little precision to avoid overflow */
        const int ONE = 1;
        const double CONST_SCALE = (double)(ONE << CONST_BITS);

        /* Convert a positive real constant to an integer scaled by CONST_SCALE.
         * IMPORTANT: if your compiler doesn't do this arithmetic at compile time,
         * you will pay a significant penalty in run time.    In that case, figure
         * the correct integer constant values and insert them by hand.
         */

        static int FIX(double x)
        {
            return (int)((x) * (double)CONST_SCALE + 0.5);
        }

        /* When adding two opposite-signed fixes, the 0.5 cancels */
        static int FIX2(double x)
        {
            return (int)((x) * (double)CONST_SCALE);
        }

        const int FIX_0_211164243 = (int)(0.5 + 0.211164243 * CONST_SCALE);
        const int FIX_0_298631336 = (int)(0.5 + 0.298631336 * CONST_SCALE);
        const int FIX_0_390180644 = (int)(0.5 + 0.390180644 * CONST_SCALE);
        const int FIX_0_509795579 = (int)(0.5 + 0.509795579 * CONST_SCALE);
        const int FIX_0_541196100 = (int)(0.5 + 0.541196100 * CONST_SCALE);
        const int FIX_0_765366865 = (int)(0.5 + 0.765366865 * CONST_SCALE);
        const int FIX_0_785694958 = (int)(0.5 + 0.785694958 * CONST_SCALE);
        const int FIX_0_899976223 = (int)(0.5 + 0.899976223 * CONST_SCALE);
        const int FIX_1_061594337 = (int)(0.5 + 1.061594337 * CONST_SCALE);
        const int FIX_1_175875602 = (int)(0.5 + 1.175875602 * CONST_SCALE);
        const int FIX_1_306562965 = (int)(0.5 + 1.306562965 * CONST_SCALE);
        const int FIX_1_451774981 = (int)(0.5 + 1.451774981 * CONST_SCALE);
        const int FIX_1_501321110 = (int)(0.5 + 1.501321110 * CONST_SCALE);
        const int FIX_1_847759065 = (int)(0.5 + 1.847759065 * CONST_SCALE);
        const int FIX_1_961570560 = (int)(0.5 + 1.961570560 * CONST_SCALE);
        const int FIX_2_053119869 = (int)(0.5 + 2.053119869 * CONST_SCALE);
        const int FIX_2_172734803 = (int)(0.5 + 2.172734803 * CONST_SCALE);
        const int FIX_2_562915447 = (int)(0.5 + 2.562915447 * CONST_SCALE);
        const int FIX_3_072711026 = (int)(0.5 + 3.072711026 * CONST_SCALE);


        const int FIX2_0_275899379 = (int)(0.275899379 * CONST_SCALE);
        const int FIX2_0_275899380 = (int)(0.275899380 * CONST_SCALE);
        const int FIX2_0_509795578 = (int)(0.509795578 * CONST_SCALE);
        const int FIX2_0_601344887 = (int)(0.601344887 * CONST_SCALE);
        const int FIX2_0_785694958 = (int)(0.785694958 * CONST_SCALE);
        const int FIX2_1_111140466 = (int)(1.111140466 * CONST_SCALE);
        const int FIX2_1_306562965 = (int)(1.306562965 * CONST_SCALE);
        const int FIX2_1_387039845 = (int)(1.387039845 * CONST_SCALE);
        const int FIX2_1_662939224 = (int)(1.662939224 * CONST_SCALE);
        const int FIX2_1_662939225 = (int)(1.662939225 * CONST_SCALE);


        /* Descale and correctly round an INT32 value that's scaled by N bits.
         * We assume RIGHT_SHIFT rounds towards minus infinity, so adding
         * the fudge factor is correct for either sign of X.
         */

        static int DESCALE(int x, int n)
        {
            return ((x) + (ONE << ((n) - 1))) >> n;
        }

        /* Multiply an INT32 variable by an INT32 constant to yield an INT32 result.
         * For 8-bit samples with the recommended scaling, all the variable
         * and constant values involved are no more than 16 bits wide, so a
         * 16x16->32 bit multiply can be used instead of a full 32x32 multiply;
         * this provides a useful speedup on many machines.
         * There is no way to specify a 16x16->32 multiply in portable C, but
         * some C compilers will do the right thing if you provide the correct
         * combination of casts.
         * NB: for 12-bit samples, a full 32-bit multiplication will be needed.
         */
        const int SPARSE_SCALE_FACTOR = 8;

        /* Precomputed idct value arrays. */

        static DCTBLOCK[] PreIDCT = new DCTBLOCK[64];


        /*
         *--------------------------------------------------------------
         *
         * init_pre_idct --
         *
         *    Pre-computes singleton coefficient IDCT values.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        public static void init_pre_idct()
        {
            int i;
            short[] initShorts = new short[64];

            for (i = 0; i < 64; i++)
            {
                initShorts[i] = 1 << SPARSE_SCALE_FACTOR;
                PreIDCT[i] = new DCTBLOCK(initShorts);
                j_rev_dct(ref PreIDCT[i]);
                initShorts[i] = 0;
            }
        }

        /*
         *--------------------------------------------------------------
         *
         * j_rev_dct_sparse --
         *
         *    Performs the inverse DCT on one block of coefficients.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        private static void sparse_scale_row(ref DCTROW row, int coef)
        {
            row.v0 = (short)((row.v0 * coef) >> SPARSE_SCALE_FACTOR);
            row.v1 = (short)((row.v1 * coef) >> SPARSE_SCALE_FACTOR);
            row.v2 = (short)((row.v2 * coef) >> SPARSE_SCALE_FACTOR);
            row.v3 = (short)((row.v3 * coef) >> SPARSE_SCALE_FACTOR);
            row.v4 = (short)((row.v4 * coef) >> SPARSE_SCALE_FACTOR);
            row.v5 = (short)((row.v5 * coef) >> SPARSE_SCALE_FACTOR);
            row.v6 = (short)((row.v6 * coef) >> SPARSE_SCALE_FACTOR);
            row.v7 = (short)((row.v7 * coef) >> SPARSE_SCALE_FACTOR);
        }

        public static void j_rev_dct_sparse(ref DCTBLOCK data, int pos)
        {
            short val;
            int v;
            int quant;

            /* If DC Coefficient. */
            if (pos == 0)
            {
                v = data.r0.v0;
                quant = 8;

                val = (short)((v + (quant >> 1)) / quant);

                data = new DCTBLOCK(val);
                return;
            }

            /* Some other coefficient. */
            int coef = data.Element(pos);
            data = PreIDCT[pos];
            sparse_scale_row(ref data.r0, coef);
            sparse_scale_row(ref data.r1, coef);
            sparse_scale_row(ref data.r2, coef);
            sparse_scale_row(ref data.r3, coef);
            sparse_scale_row(ref data.r4, coef);
            sparse_scale_row(ref data.r5, coef);
            sparse_scale_row(ref data.r6, coef);
            sparse_scale_row(ref data.r7, coef);
        }

        /*
         *--------------------------------------------------------------
         *
         * j_rev_dct --
         *
         *    The inverse DCT function.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         * Profiling results:
         *    This function only takes about 0.01ms per call, but is called many 
         *    many times, taking about 30% of the total time used by playback.
         *
         *--------------------------------------------------------------
         */

        private static void j_rev_dct_row_check_zero(ref short c0, ref short c1, ref short c2, ref short c3, ref short c4, ref short c5, ref short c6, ref short c7, int scaleBits)
        {
            if (c1 == 0 &&
                c2 == 0 &&
                c3 == 0 &&
                c4 == 0 &&
                c5 == 0 &&
                c6 == 0 &&
                c7 == 0)
            /* AC terms all zero */
            {
                c0 = c1 = c2 = c3 = c4 = c5 = c6 = c7 = ((short)(c0 << (-scaleBits)));
            }
            else
                j_rev_dct_row(ref c0, ref c1, ref c2, ref c3, ref c4, ref c5, ref c6, ref c7, scaleBits);
        }

        private static void j_rev_dct_row_check_zero(ref DCTROW row, int scaleBits)
        {
            j_rev_dct_row_check_zero(ref row.v0, ref row.v1, ref row.v2, ref row.v3, ref row.v4, ref row.v5, ref row.v6, ref row.v7, scaleBits);
        }


        private static void j_rev_dct_row(ref short c0, ref short c1, ref short c2, ref short c3, ref short c4, ref short c5, ref short c6, ref short c7, int scaleBits)
        {
            /* Pass 1: process rows. */
            /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
            /* furthermore, we scale the results by 2**PASS1_BITS. */
            /* Due to quantization, we will usually find that many of the input
             * coefficients are zero, especially the AC terms.    We can exploit this
             * by short-circuiting the IDCT calculation for any row in which all
             * the AC terms are zero.    In that case each output is equal to the
             * DC coefficient (with scale factor as needed).
             * With typical images and quantization tables, half or more of the
             * row DCT calculations can be simplified this way.
             */

            Int32 tmp0, tmp1, tmp2, tmp3;
            Int32 tmp10, tmp11, tmp12, tmp13;
            Int32 z1, z2, z3, z4, z5;
            Int32 d0, d1, d2, d3, d4, d5, d6, d7;

            d0 = c0;
            d1 = c1;
            d2 = c2;
            d3 = c3;
            d4 = c4;
            d5 = c5;
            d6 = c6;
            d7 = c7;

            /* Even part: reverse the even part of the forward DCT. */
            /* The rotator is sqrt(2)*c(-6). */
            if (d6 != 0)
            {
                if (d4 != 0)
                {
                    if (d2 != 0)
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
                            z1 = (d2 + d6) * FIX_0_541196100;
                            tmp2 = z1 + d6 * (-FIX_1_847759065);
                            tmp3 = z1 + d2 * FIX_0_765366865;

                            tmp0 = (d0 + d4) << CONST_BITS;
                            tmp1 = (d0 - d4) << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp1 + tmp2;
                            tmp12 = tmp1 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */
                            z1 = (d2 + d6) * FIX_0_541196100;
                            tmp2 = z1 + d6 * (-FIX_1_847759065);
                            tmp3 = z1 + d2 * FIX_0_765366865;

                            tmp0 = d4 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp2 - tmp0;
                            tmp12 = -(tmp0 + tmp2);
                        }
                    }
                    else
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
                            tmp2 = d6 * (-FIX2_1_306562965);
                            tmp3 = d6 * FIX_0_541196100;

                            tmp0 = (d0 + d4) << CONST_BITS;
                            tmp1 = (d0 - d4) << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp1 + tmp2;
                            tmp12 = tmp1 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */
                            tmp2 = d6 * (-FIX2(1.306562965));
                            tmp3 = d6 * FIX_0_541196100;

                            tmp0 = d4 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp2 - tmp0;
                            tmp12 = -(tmp0 + tmp2);
                        }
                    }
                }
                else
                {
                    if (d2 != 0)
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */
                            z1 = (d2 + d6) * FIX_0_541196100;
                            tmp2 = z1 + d6 * (-FIX_1_847759065);
                            tmp3 = z1 + d2 * FIX_0_765366865;

                            tmp0 = d0 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp0 + tmp2;
                            tmp12 = tmp0 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */
                            z1 = (d2 + d6) * FIX_0_541196100;
                            tmp2 = z1 + d6 * (-FIX_1_847759065);
                            tmp3 = z1 + d2 * FIX_0_765366865;

                            tmp10 = tmp3;
                            tmp13 = -tmp3;
                            tmp11 = tmp2;
                            tmp12 = -tmp2;
                        }
                    }
                    else
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */
                            tmp2 = d6 * (-FIX2_1_306562965);
                            tmp3 = d6 * FIX_0_541196100;

                            tmp0 = d0 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp0 + tmp2;
                            tmp12 = tmp0 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */
                            tmp2 = d6 * (-FIX2_1_306562965);
                            tmp3 = d6 * FIX_0_541196100;

                            tmp10 = tmp3;
                            tmp13 = -tmp3;
                            tmp11 = tmp2;
                            tmp12 = -tmp2;
                        }
                    }
                }
            }
            else
            {
                if (d4 != 0)
                {
                    if (d2 != 0)
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
                            tmp2 = d2 * FIX_0_541196100;
                            tmp3 = d2 * FIX_1_306562965;

                            tmp0 = (d0 + d4) << CONST_BITS;
                            tmp1 = (d0 - d4) << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp1 + tmp2;
                            tmp12 = tmp1 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */
                            tmp2 = d2 * FIX_0_541196100;
                            tmp3 = d2 * FIX_1_306562965;

                            tmp0 = d4 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp2 - tmp0;
                            tmp12 = -(tmp0 + tmp2);
                        }
                    }
                    else
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
                            tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
                            tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
                        }
                        else
                        {
                            /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */
                            tmp10 = tmp13 = d4 << CONST_BITS;
                            tmp11 = tmp12 = -tmp10;
                        }
                    }
                }
                else
                {
                    if (d2 != 0)
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */
                            tmp2 = d2 * FIX_0_541196100;
                            tmp3 = d2 * FIX_1_306562965;

                            tmp0 = d0 << CONST_BITS;

                            tmp10 = tmp0 + tmp3;
                            tmp13 = tmp0 - tmp3;
                            tmp11 = tmp0 + tmp2;
                            tmp12 = tmp0 - tmp2;
                        }
                        else
                        {
                            /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */
                            tmp2 = d2 * FIX_0_541196100;
                            tmp3 = d2 * FIX_1_306562965;

                            tmp10 = tmp3;
                            tmp13 = -tmp3;
                            tmp11 = tmp2;
                            tmp12 = -tmp2;
                        }
                    }
                    else
                    {
                        if (d0 != 0)
                        {
                            /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */
                            tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS;
                        }
                        else
                        {
                            /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */
                            tmp10 = tmp13 = tmp11 = tmp12 = 0;
                        }
                    }
                }
            }


            /* Odd part per figure 8; the matrix is unitary and hence its
             * transpose is its inverse.    i0..i3 are y7,y5,y3,y1 respectively.
             */

            if (d7 != 0)
            {
                if (d5 != 0)
                {
                    if (d3 != 0)
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z4 = d5 + d1;
                            z5 = (z3 + z4) * FIX_1_175875602;

                            tmp0 = d7 * FIX_0_298631336;
                            tmp1 = d5 * FIX_2_053119869;
                            tmp2 = d3 * FIX_3_072711026;
                            tmp3 = d1 * FIX_1_501321110;
                            z1 = z1 * (-FIX_0_899976223);
                            z2 = z2 * (-FIX_2_562915447);
                            z3 = z3 * (-FIX_1_961570560);
                            z4 = z4 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        }
                        else
                        {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z5 = (z3 + d5) * FIX_1_175875602;

                            tmp0 = d7 * FIX_0_298631336;
                            tmp1 = d5 * FIX_2_053119869;
                            tmp2 = d3 * FIX_3_072711026;
                            z1 = d7 * (-FIX_0_899976223);
                            z2 = z2 * (-FIX_2_562915447);
                            z3 = z3 * (-FIX_1_961570560);
                            z4 = d5 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    }
                    else
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z4 = d5 + d1;
                            z5 = (d7 + z4) * FIX_1_175875602;

                            tmp0 = d7 * FIX_0_298631336;
                            tmp1 = d5 * FIX_2_053119869;
                            tmp3 = d1 * FIX_1_501321110;
                            z1 = z1 * (-FIX_0_899976223);
                            z2 = d5 * (-FIX_2_562915447);
                            z3 = d7 * (-FIX_1_961570560);
                            z4 = z4 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 = z2 + z3;
                            tmp3 += z1 + z4;
                        }
                        else
                        {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
                            z5 = (d7 + d5) * FIX_1_175875602;

                            tmp0 = d7 * (-FIX2_0_601344887);
                            tmp1 = d5 * (-FIX2_0_509795578);
                            z1 = d7 * (-FIX_0_899976223);
                            z3 = d7 * (-FIX_1_961570560);
                            z2 = d5 * (-FIX_2_562915447);
                            z4 = d5 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z3;
                            tmp1 += z4;
                            tmp2 = z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    }
                }
                else
                {
                    if (d3 != 0)
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z3 = d7 + d3;
                            z5 = (z3 + d1) * FIX_1_175875602;

                            tmp0 = d7 * FIX_0_298631336;
                            tmp2 = d3 * FIX_3_072711026;
                            tmp3 = d1 * FIX_1_501321110;
                            z1 = z1 * (-FIX_0_899976223);
                            z2 = d3 * (-FIX_2_562915447);
                            z3 = z3 * (-FIX_1_961570560);
                            z4 = d1 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 = z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        }
                        else
                        {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
                            z3 = d7 + d3;
                            z5 = z3 * FIX_1_175875602;

                            tmp0 = d7 * (-FIX2_0_601344887);
                            tmp2 = d3 * FIX_0_509795579;
                            z1 = d7 * (-FIX_0_899976223);
                            z2 = d3 * (-FIX_2_562915447);
                            z3 = z3 * (-FIX2_0_785694958);

                            tmp0 += z3;
                            tmp1 = z2 + z5;
                            tmp2 += z3;
                            tmp3 = z1 + z5;
                        }
                    }
                    else
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z5 = z1 * FIX_1_175875602;

                            tmp0 = d7 * (-FIX2_1_662939224);
                            tmp3 = d1 * FIX2_1_111140466;
                            z1 = z1 * FIX2_0_275899379;
                            z3 = d7 * (-FIX_1_961570560);
                            z4 = d1 * (-FIX_0_390180644);

                            tmp0 += z1;
                            tmp1 = z4 + z5;
                            tmp2 = z3 + z5;
                            tmp3 += z1;
                        }
                        else
                        {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
                            tmp0 = d7 * (-FIX2_1_387039845);
                            tmp1 = d7 * FIX_1_175875602;
                            tmp2 = d7 * (-FIX2_0_785694958);
                            tmp3 = d7 * FIX2_0_275899379;
                        }
                    }
                }
            }
            else
            {
                if (d5 != 0)
                {
                    if (d3 != 0)
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;
                            z4 = d5 + d1;
                            z5 = (d3 + z4) * FIX_1_175875602;

                            tmp1 = d5 * FIX_2_053119869;
                            tmp2 = d3 * FIX_3_072711026;
                            tmp3 = d1 * FIX_1_501321110;
                            z1 = d1 * (-FIX_0_899976223);
                            z2 = z2 * (-FIX_2_562915447);
                            z3 = d3 * (-FIX_1_961570560);
                            z4 = z4 * (-FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 = z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        }
                        else
                        {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;
                            z5 = z2 * FIX_1_175875602;

                            tmp1 = d5 * FIX2_1_662939225;
                            tmp2 = d3 * FIX2_1_111140466;
                            z2 = z2 * (-FIX2_1_387039845);
                            z3 = d3 * (-FIX_1_961570560);
                            z4 = d5 * (-FIX_0_390180644);

                            tmp0 = z3 + z5;
                            tmp1 += z2;
                            tmp2 += z2;
                            tmp3 = z4 + z5;
                        }
                    }
                    else
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
                            z4 = d5 + d1;
                            z5 = z4 * FIX_1_175875602;

                            tmp1 = d5 * (-FIX2_0_509795578);
                            tmp3 = d1 * FIX2_0_601344887;
                            z1 = d1 * (-FIX_0_899976223);
                            z2 = d5 * (-FIX_2_562915447);
                            z4 = z4 * FIX2_0_785694958;

                            tmp0 = z1 + z5;
                            tmp1 += z4;
                            tmp2 = z2 + z5;
                            tmp3 += z4;
                        }
                        else
                        {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
                            tmp0 = d5 * FIX_1_175875602;
                            tmp1 = d5 * FIX2_0_275899380;
                            tmp2 = d5 * (-FIX2_1_387039845);
                            tmp3 = d5 * FIX2_0_785694958;
                        }
                    }
                }
                else
                {
                    if (d3 != 0)
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
                            z5 = d3 + d1;

                            tmp2 = d3 * (-FIX_1_451774981);
                            tmp3 = d1 * (FIX_0_211164243 - 1);     // EJL: Used to include a -1?
                            z1 = d1 * FIX_1_061594337;
                            z2 = d3 * (-FIX_2_172734803);
                            z4 = z5 * FIX_0_785694958;
                            z5 = z5 * FIX_1_175875602;

                            tmp0 = z1 - z4;
                            tmp1 = z2 + z4;
                            tmp2 += z5;
                            tmp3 += z5;
                        }
                        else
                        {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
                            tmp0 = d3 * (-FIX2_0_785694958);
                            tmp1 = d3 * (-FIX2_1_387039845);
                            tmp2 = d3 * (-FIX2_0_275899379);
                            tmp3 = d3 * FIX_1_175875602;
                        }
                    }
                    else
                    {
                        if (d1 != 0)
                        {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = d1 * FIX2_0_275899379;
                            tmp1 = d1 * FIX2_0_785694958;
                            tmp2 = d1 * FIX_1_175875602;
                            tmp3 = d1 * FIX2_1_387039845;
                        }
                        else
                        {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = tmp1 = tmp2 = tmp3 = 0;
                        }
                    }
                }
            }

            DCTROW outRow;
            /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */
            int descaleBits = CONST_BITS + scaleBits;
            c0 = (short)DESCALE(tmp10 + tmp3, descaleBits);
            c7 = (short)DESCALE(tmp10 - tmp3, descaleBits);
            c1 = (short)DESCALE(tmp11 + tmp2, descaleBits);
            c6 = (short)DESCALE(tmp11 - tmp2, descaleBits);
            c2 = (short)DESCALE(tmp12 + tmp1, descaleBits);
            c5 = (short)DESCALE(tmp12 - tmp1, descaleBits);
            c3 = (short)DESCALE(tmp13 + tmp0, descaleBits);
            c4 = (short)DESCALE(tmp13 - tmp0, descaleBits);
        }

        public static void j_rev_dct(ref DCTBLOCK data)
        {
            /* Pass 1: process rows. */
            /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
            /* furthermore, we scale the results by 2**PASS1_BITS. */

            j_rev_dct_row_check_zero(ref data.r0, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r1, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r2, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r3, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r4, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r5, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r6, -PASS1_BITS);
            j_rev_dct_row_check_zero(ref data.r7, -PASS1_BITS);

            j_rev_dct_row(ref data.r0.v0, ref data.r1.v0, ref data.r2.v0, ref data.r3.v0, ref data.r4.v0, ref data.r5.v0, ref data.r6.v0, ref data.r7.v0, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v1, ref data.r1.v1, ref data.r2.v1, ref data.r3.v1, ref data.r4.v1, ref data.r5.v1, ref data.r6.v1, ref data.r7.v1, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v2, ref data.r1.v2, ref data.r2.v2, ref data.r3.v2, ref data.r4.v2, ref data.r5.v2, ref data.r6.v2, ref data.r7.v2, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v3, ref data.r1.v3, ref data.r2.v3, ref data.r3.v3, ref data.r4.v3, ref data.r5.v3, ref data.r6.v3, ref data.r7.v3, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v4, ref data.r1.v4, ref data.r2.v4, ref data.r3.v4, ref data.r4.v4, ref data.r5.v4, ref data.r6.v4, ref data.r7.v4, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v5, ref data.r1.v5, ref data.r2.v5, ref data.r3.v5, ref data.r4.v5, ref data.r5.v5, ref data.r6.v5, ref data.r7.v5, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v6, ref data.r1.v6, ref data.r2.v6, ref data.r3.v6, ref data.r4.v6, ref data.r5.v6, ref data.r6.v6, ref data.r7.v6, PASS1_BITS + 3);
            j_rev_dct_row(ref data.r0.v7, ref data.r1.v7, ref data.r2.v7, ref data.r3.v7, ref data.r4.v7, ref data.r5.v7, ref data.r6.v7, ref data.r7.v7, PASS1_BITS + 3);

        }
    }
}
